package com.zh.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.constants.Constants;
import com.zh.constants.ErrorCode;
import com.zh.mapper.DriverMapper;
import com.zh.mapstruct.DriverStructMapper;
import com.zh.pojo.bo.DriverSettingBo;
import com.zh.pojo.domain.Driver;
import com.zh.pojo.domain.DriverSetting;
import com.zh.pojo.domain.DriverSummary;
import com.zh.pojo.dto.LocationDto;
import com.zh.pojo.dto.WxMinAppRegisterDto;
import com.zh.pojo.vo.DriverWorkBenchVo;
import com.zh.remote.api.LoginApi;
import com.zh.remote.pojo.param.CreateLoginDto;
import com.zh.result.JSONResult;
import com.zh.service.IDriverService;
import com.zh.service.IDriverSettingService;
import com.zh.service.IDriverSummaryService;
import com.zh.service.IDriverWalletService;
import com.zh.template.WeChatTemplate;
import com.zh.utils.AssertUtil;
import com.zh.utils.BitStatesUtils;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;

/**
 * <p>
 * 司机对象 服务实现类
 * </p>
 *
 * @author 初四
 * @since 2024-06-13
 */
@Service
@Slf4j
public class DriverServiceImpl extends ServiceImpl<DriverMapper, Driver> implements IDriverService {
    private static final Date nowDate = new Date();

    @Autowired
    private IDriverSettingService settingService;

    @Autowired
    private IDriverSummaryService summaryService;

    @Autowired
    private IDriverWalletService walletService;

    @Autowired
    private WeChatTemplate weChatTemplate;

    @Autowired
    private LoginApi loginApi;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;


    /**
     * 将司机坐标缓存到GEO
     */
    @Override
    public void cacheLocationGeo(LocationDto locationDto) {
        // 缓存司机坐标到GEO
        Point point = new Point(locationDto.getLongitude(), locationDto.getLatitude());
        redisTemplate.opsForGeo().add(Constants.Redis.DRIVER_GEO_KEY, point, StpUtil.getLoginIdAsLong());

    }

    

    /**
     * 停止接单
     */
    @Override
    public void stopReceivingOrders() {
        // 删除redis中的司机配置信息
        String key = String.format(Constants.Redis.DRIVER_SETTING_KEY, StpUtil.getLoginIdAsLong());
        // 如果司机十分钟没有操作则自动删除
        redisTemplate.delete(key);
        // 删除redis中 司机GEO坐标
        redisTemplate.opsForGeo().remove(Constants.Redis.DRIVER_GEO_KEY,StpUtil.getLoginIdAsLong());
    }
    /**
     * 司机开始接单
     */
    @Override
    public void startReceivingOrders() {
        DriverSetting driverSetting = settingService.getById(StpUtil.getLoginIdAsLong());
        AssertUtil.isNotNull(driverSetting, "司机配置信息不存在");
        DriverSettingBo settingBo = DriverStructMapper.INSTANCE.driverSettingConvertToDriverSettingBo(driverSetting);
        // 将司机配置存到redis中
        String key = String.format(Constants.Redis.DRIVER_SETTING_KEY,StpUtil.getLoginIdAsLong());
        // 如果司机十分钟没有操作则自动删除
//        redisTemplate.opsForValue().set(key, settingBo,10, TimeUnit.MINUTES);
        redisTemplate.opsForValue().set(key, settingBo);
    }


    /**
     * 加载司机工作台信息
     * @return
     */
    @Override
    public DriverWorkBenchVo selectByDriverWorkbench() {
        // 1、通过satoken 获取司机Id, 并查询司机实名信息，获取 司机实名状态
        Long driverId = StpUtil.getLoginIdAsLong();
        Driver driver = super.getById(driverId);
        boolean hasState = BitStatesUtils.hasState(driver.getBitState(), BitStatesUtils.OP_REAL_AUTHENTICATIONED);
        // 2、查询司机汇总
        DriverSummary driverSummary = summaryService.getById(driverId);
        DriverWorkBenchVo driverWorkBenchVo = DriverStructMapper.INSTANCE.driverSummaryConvertToDriverWorkBenchVo(driverSummary);
        driverWorkBenchVo.setRealAuthSuccess(hasState);
        return driverWorkBenchVo;
    }


    /**
     * 微信小程序司机注册
     * 1、获取前端传来的code凭证
     * 2、传入AppID(小程序ID) AppSecret(小程序密钥)
     * 3、通过获取的三个参数拼接url https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type
     * =authorization_code ,拼接好Url地址后通过RestTemplate 发送get请求
     * 4、拿到openId （用户唯一标识）
     * 5、判断openId是否存在 如果存在说明已注册
     * 6、保存 司机用户信息， 保存司机配置、保存司机结算对象、保存司机钱包
     * 7、 通过feign 远程调用认证服务uaa 保存login信息
     *
     * @param wxRegisterDto 封装的传递参数(code，phone)
     */
    @Override
    // 开启全局事务,只在在入口方法贴注解 即可
    @GlobalTransactional(name = "driver-wxregister", rollbackFor = Exception.class)
    public void wxregister(WxMinAppRegisterDto wxRegisterDto) {
        // 1、获取 OpenId
        String openId = weChatTemplate.getOpenId(wxRegisterDto.getCode());
        // 2、 判断司机 是否注册成功
        Driver driver = selectDriver(openId);
        // 3、保存司机相关对象
        saveDriverAssociation(driver);
        // 4、通过feign 远程调用认证服务uaa 保存login信息
        createLogin(driver);

    }
    /**
     * 保存司机关联对象
     *
     * @param driver 　　司机对象
     */
    private void saveDriverAssociation(Driver driver) {
        // 保存司机配置
        settingService.saveDriverSettings(driver.getId());
        // 保存司机结算对象
        summaryService.saveDriverSummary(driver.getId());
        // 保存司机钱包
        walletService.saveDriverWallet(driver.getId());
    }

    /**
     * 根据openId 查询数据库司机对象
     *
     * @param openId
     * @return
     */
    private Driver selectDriver(String openId) {
        // 拿着OpenId 去数据库查询， 如果存在openId 说明 司机用户已注册，
        LambdaQueryWrapper<Driver> queryWrapper = new LambdaQueryWrapper<Driver>().eq(Driver::getOpenId, openId);
        Driver driver = super.getOne(queryWrapper);
        log.info("Driver : {}" + driver);
        // 使用断言工具+ 错误信息封装类 判断openId是否存在  果存在openId 说明 司机用户已注册（账号已存在），
        AssertUtil.isNull(driver, ErrorCode.PARAM_PHONE_EXIST.getMessage());
        //6、OpenId不存在则保存 司机用户信息，保存司机配置、保存司机结算对象、保存司机钱包
        driver = saveDriver(openId);
        return driver;
    }

    /**
     * 保存login 信息
     *
     * @param driver
     */
    private void createLogin(Driver driver) {
        CreateLoginDto loginDto = new CreateLoginDto();
        loginDto.setId(driver.getId());
        loginDto.setUsername(driver.getPhone());
        loginDto.setType(Constants.Login.TYPE_DRIVER);
        loginDto.setOpenId(driver.getOpenId());
        loginDto.setPhone(driver.getPhone());
        // 这里远程调用 LoginApi  Feign接口
        JSONResult jsonResult = loginApi.createLogin(loginDto);
        AssertUtil.isTrue(jsonResult.isSuccess(), "远程保存Login失败");
    }

    /**
     * 保存司机信息
     *
     * @param openId
     * @return
     */
    private Driver saveDriver(String openId) {
        Driver driver = new Driver();
        driver.setPhone("");
        driver.setCreateTime(new Date());
        driver.setOpenId(openId);
        driver.setPhone("18381364990");
        // 给司机添加手机认证的位状态
        driver.setBitState(BitStatesUtils.addState(0L, BitStatesUtils.OP_PHONE));
        super.save(driver);
        return driver;
    }
}
